"""
Event handlers for the continual learning framework.

This module defines interfaces for event handlers that can process events
in both synchronous and asynchronous modes.
"""

from functools import wraps
from typing import Any, Callable, Coroutine, Generic, Optional, Union

from .event_types import EventType, E


class EventHandler(Generic[E]):
    """
    Base class for event handlers.

    Event handlers process events of a specific type. They can be registered with
    an EventDispatcher to be notified when events occur.
    """

    def __init__(
        self,
        event_type: EventType,
        handler_func: Callable[[E], None],
        name: Optional[str] = None,
        priority: int = 0,
    ):
        """
        Initialize an event handler.

        Args:
            event_type: Type of event this handler processes
            handler_func: Function to call when an event occurs
            name: Optional name for this handler
            priority: Priority of this handler (higher numbers execute first)
        """
        self.event_type = event_type
        self.handler_func = handler_func
        self.name = name or f"{self.__class__.__name__}_{id(self)}"
        self.priority = priority

    def __call__(self, event: E) -> None:
        """
        Process an event.

        Args:
            event: Event data to process
        """
        if event.event_type != self.event_type:
            raise ValueError(
                f"Event type mismatch: expected {self.event_type}, got {event.event_type}"
            )

        self.handler_func(event)

    def __lt__(self, other: "EventHandler") -> bool:
        """
        Compare handlers by priority.

        This enables handlers to be sorted by priority.

        Args:
            other: Another handler to compare with

        Returns:
            bool: True if this handler has lower priority than the other
        """
        if not isinstance(other, EventHandler):
            return NotImplemented
        return self.priority < other.priority


class AsyncEventHandler(Generic[E]):
    """
    Asynchronous event handler.

    This handler processes events asynchronously using coroutines.
    """

    def __init__(
        self,
        event_type: EventType,
        handler_func: Callable[[E], Coroutine[Any, Any, None]],
        name: Optional[str] = None,
        priority: int = 0,
    ):
        """
        Initialize an asynchronous event handler.

        Args:
            event_type: Type of event this handler processes
            handler_func: Coroutine function to call when an event occurs
            name: Optional name for this handler
            priority: Priority of this handler (higher numbers execute first)
        """
        self.event_type = event_type
        self.handler_func = handler_func
        self.name = name or f"{self.__class__.__name__}_{id(self)}"
        self.priority = priority

    async def __call__(self, event: E) -> None:
        """
        Process an event asynchronously.

        Args:
            event: Event data to process
        """
        if event.event_type != self.event_type:
            raise ValueError(
                f"Event type mismatch: expected {self.event_type}, got {event.event_type}"
            )

        await self.handler_func(event)

    def __lt__(self, other: "AsyncEventHandler") -> bool:
        """
        Compare handlers by priority.

        This enables handlers to be sorted by priority.

        Args:
            other: Another handler to compare with

        Returns:
            bool: True if this handler has lower priority than the other
        """
        if not isinstance(other, AsyncEventHandler):
            return NotImplemented
        return self.priority < other.priority


# Define handler types
Handler = Union[EventHandler[E], AsyncEventHandler[E]]


# Helper functions to create decorators for event handlers
def on_event(
    event_type: EventType,
    dispatcher=None,
    name: Optional[str] = None,
    priority: int = 0,
):
    """
    Decorator to create and register an event handler.

    This decorator creates an EventHandler from a function and optionally
    registers it with a dispatcher.

    Args:
        event_type: Type of event to handle
        dispatcher: Optional event dispatcher to register with
        name: Optional name for the handler
        priority: Priority for handler execution (higher numbers execute first)

    Returns:
        Decorator function that wraps the handler function

    Example:
        ```python
        @on_event(EventType.TASK_START, dispatcher)
        def handle_task_start(event):
            print(f"Task {event.task_id} started")
        ```
    """

    def decorator(func: Callable[[E], None]) -> Callable[[E], None]:
        handler = EventHandler(
            event_type, func, name=name or func.__name__, priority=priority
        )

        if dispatcher is not None:
            dispatcher.register_handler(handler)

        @wraps(func)
        def wrapper(event: E) -> None:
            return handler(event)

        # Attach the handler to the function for later access
        wrapper.handler = handler
        return wrapper

    return decorator


def on_event_async(
    event_type: EventType,
    dispatcher=None,
    name: Optional[str] = None,
    priority: int = 0,
):
    """
    Decorator to create and register an asynchronous event handler.

    This decorator creates an AsyncEventHandler from a coroutine function
    and optionally registers it with a dispatcher.

    Args:
        event_type: Type of event to handle
        dispatcher: Optional event dispatcher to register with
        name: Optional name for the handler
        priority: Priority for handler execution (higher numbers execute first)

    Returns:
        Decorator function that wraps the handler coroutine

    Example:
        ```python
        @on_event_async(EventType.TASK_END, async_dispatcher)
        async def handle_task_end(event):
            await asyncio.sleep(1)
            print(f"Task {event.task_id} ended")
        ```
    """

    def decorator(
        func: Callable[[E], Coroutine[Any, Any, None]],
    ) -> Callable[[E], Coroutine[Any, Any, None]]:
        handler = AsyncEventHandler(
            event_type, func, name=name or func.__name__, priority=priority
        )

        if dispatcher is not None:
            dispatcher.register_handler(handler)

        @wraps(func)
        async def wrapper(event: E) -> None:
            return await handler(event)

        # Attach the handler to the function for later access
        wrapper.handler = handler
        return wrapper

    return decorator
